<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>IP地址计算器</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.8/dist/chart.umd.min.js"></script>
    
    <!-- Tailwind配置 -->
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#165DFF',
                        secondary: '#36CBCB',
                        neutral: '#F5F7FA',
                        dark: '#1D2129',
                    },
                    fontFamily: {
                        inter: ['Inter', 'sans-serif'],
                    },
                }
            }
        }
    </script>
    
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto {
                content-visibility: auto;
            }
            .input-focus {
                @apply focus:ring-2 focus:ring-primary/50 focus:border-primary;
            }
            .card-hover {
                @apply hover:shadow-lg hover:transform hover:-translate-y-1 transition-all duration-300;
            }
            .grid-subnet {
                display: grid;
                grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
                gap: 1rem;
            }
        }
    </style>
</head>
<body class="font-inter bg-gradient-to-br from-neutral to-white min-h-screen">
    <div class="container mx-auto px-4 py-8 max-w-6xl">
        <!-- 标题区域 -->
        <header class="text-center mb-10">
            <h1 class="text-[clamp(1.8rem,4vw,2.5rem)] font-bold text-transparent bg-clip-text bg-gradient-to-r from-primary to-secondary mb-3">
                IP地址计算器
            </h1>
            <p class="text-gray-600 max-w-2xl mx-auto">
                输入IP地址和子网掩码，计算网络地址、广播地址和可用主机范围
            </p>
        </header>
        
        <!-- 主内容区 -->
        <main class="bg-white rounded-2xl shadow-xl p-6 md:p-10">
            <!-- 输入表单 -->
            <div class="mb-10">
                <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                    <!-- IP地址输入 -->
                    <div class="bg-neutral rounded-xl p-6 card-hover">
                        <label class="block text-sm font-medium text-gray-700 mb-2">
                            IP地址
                        </label>
                        <div class="flex items-center space-x-2">
                            <input type="text" id="ipAddress" placeholder="例如: 192.168.1.1" 
                                   class="flex-1 rounded-lg border-gray-300 shadow-sm input-focus p-3"
                                   value="192.168.1.1">
                            <span class="text-gray-500">/</span>
                            <select id="cidrPrefix" class="rounded-lg border-gray-300 shadow-sm input-focus p-3 w-20">
                                <option value="8">8</option>
                                <option value="16">16</option>
                                <option value="24" selected>24</option>
                                <option value="25">25</option>
                                <option value="26">26</option>
                                <option value="27">27</option>
                                <option value="28">28</option>
                                <option value="29">29</option>
                                <option value="30">30</option>
                            </select>
                        </div>
                    </div>
                    
                    <!-- 子网掩码输入 -->
                    <div class="bg-neutral rounded-xl p-6 card-hover">
                        <label class="block text-sm font-medium text-gray-700 mb-2">
                            子网掩码 (自动计算)
                        </label>
                        <input type="text" id="subnetMask" placeholder="例如: 255.255.255.0" 
                               class="w-full rounded-lg border-gray-300 shadow-sm input-focus p-3 bg-gray-100"
                               readonly value="255.255.255.0">
                        
                        <div class="mt-4">
                            <label class="block text-sm font-medium text-gray-700 mb-2">
                                手动输入子网掩码
                            </label>
                            <div class="grid grid-cols-4 gap-2">
                                <input type="number" id="octet1" min="0" max="255" value="255" 
                                       class="rounded-lg border-gray-300 shadow-sm input-focus p-2 text-center">
                                <input type="number" id="octet2" min="0" max="255" value="255" 
                                       class="rounded-lg border-gray-300 shadow-sm input-focus p-2 text-center">
                                <input type="number" id="octet3" min="0" max="255" value="255" 
                                       class="rounded-lg border-gray-300 shadow-sm input-focus p-2 text-center">
                                <input type="number" id="octet4" min="0" max="255" value="0" 
                                       class="rounded-lg border-gray-300 shadow-sm input-focus p-2 text-center">
                            </div>
                            <button id="applySubnet" class="mt-3 w-full bg-primary hover:bg-primary/90 text-white rounded-lg py-2 px-4 transition-all duration-300">
                                应用子网掩码
                            </button>
                        </div>
                    </div>
                </div>
                
                <!-- 计算按钮 -->
                <div class="mt-6 text-center">
                    <button id="calculateBtn" class="bg-primary hover:bg-primary/90 text-white rounded-xl py-3 px-8 transition-all duration-300 shadow-lg hover:shadow-xl flex items-center justify-center mx-auto">
                        <i class="fa fa-calculator mr-2"></i> 计算
                    </button>
                </div>
            </div>
            
            <!-- 结果展示 -->
            <div id="results" class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-10">
                <!-- 基本信息 -->
                <div class="bg-neutral rounded-xl p-6 card-hover">
                    <h3 class="text-lg font-semibold mb-4 flex items-center">
                        <i class="fa fa-info-circle text-primary mr-2"></i> 基本信息
                    </h3>
                    <div class="space-y-3">
                        <div class="flex justify-between">
                            <span class="text-gray-600">IP类别:</span>
                            <span id="ipClass" class="font-medium">C类</span>
                        </div>
                        <div class="flex justify-between">
                            <span class="text-gray-600">网络地址:</span>
                            <span id="networkAddress" class="font-medium">192.168.1.0</span>
                        </div>
                        <div class="flex justify-between">
                            <span class="text-gray-600">广播地址:</span>
                            <span id="broadcastAddress" class="font-medium">192.168.1.255</span>
                        </div>
                        <div class="flex justify-between">
                            <span class="text-gray-600">可用主机范围:</span>
                            <span id="hostRange" class="font-medium">192.168.1.1 - 192.168.1.254</span>
                        </div>
                        <div class="flex justify-between">
                            <span class="text-gray-600">可用主机数量:</span>
                            <span id="hostCount" class="font-medium">254</span>
                        </div>
                        <div class="flex justify-between">
                            <span class="text-gray-600">二进制子网掩码:</span>
                            <span id="binaryMask" class="font-medium">11111111.11111111.11111111.00000000</span>
                        </div>
                    </div>
                </div>
                
                <!-- 子网划分 -->
                <div class="bg-neutral rounded-xl p-6 card-hover">
                    <h3 class="text-lg font-semibold mb-4 flex items-center">
                        <i class="fa fa-sitemap text-primary mr-2"></i> 子网划分
                    </h3>
                    <div class="space-y-3">
                        <div class="flex justify-between">
                            <span class="text-gray-600">子网数量:</span>
                            <span id="subnetCount" class="font-medium">1</span>
                        </div>
                        <div class="flex justify-between">
                            <span class="text-gray-600">每个子网主机数:</span>
                            <span id="hostsPerSubnet" class="font-medium">254</span>
                        </div>
                        
                        <div class="mt-4">
                            <label class="block text-sm font-medium text-gray-700 mb-2">
                                划分子网
                            </label>
                            <div class="flex space-x-2">
                                <input type="number" id="subnetSplit" min="1" max="32" value="2" 
                                       class="rounded-lg border-gray-300 shadow-sm input-focus p-2 w-full">
                                <button id="splitBtn" class="bg-secondary hover:bg-secondary/90 text-white rounded-lg py-2 px-4 transition-all duration-300">
                                    划分
                                </button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            
            <!-- 子网可视化 -->
            <div id="subnetVisualization" class="mb-10 hidden">
                <h3 class="text-lg font-semibold mb-4 flex items-center">
                    <i class="fa fa-area-chart text-primary mr-2"></i> 子网可视化
                </h3>
                <div class="bg-neutral rounded-xl p-6">
                    <canvas id="subnetChart" height="200"></canvas>
                </div>
            </div>
            
            <!-- 子网列表 -->
            <div id="subnetList" class="mb-6 hidden">
                <h3 class="text-lg font-semibold mb-4 flex items-center">
                    <i class="fa fa-list text-primary mr-2"></i> 子网详情
                </h3>
                <div class="grid-subnet" id="subnetGrid"></div>
            </div>
        </main>
        
        <!-- 学习资源 -->
        <div class="mt-10 bg-white rounded-2xl shadow-xl p-6 md:p-10">
            <h3 class="text-xl font-semibold mb-4 flex items-center">
                <i class="fa fa-book text-primary mr-2"></i> IP地址学习资源
            </h3>
            <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
                <div class="bg-neutral rounded-xl p-4 card-hover">
                    <h4 class="font-medium mb-2">IP地址分类</h4>
                    <p class="text-sm text-gray-600 mb-3">
                        A类: 0.0.0.0 - 127.255.255.255<br>
                        B类: 128.0.0.0 - 191.255.255.255<br>
                        C类: 192.0.0.0 - 223.255.255.255<br>
                        D类: 224.0.0.0 - 239.255.255.255 (多播)<br>
                        E类: 240.0.0.0 - 255.255.255.255 (保留)
                    </p>
                </div>
                <div class="bg-neutral rounded-xl p-4 card-hover">
                    <h4 class="font-medium mb-2">CIDR表示法</h4>
                    <p class="text-sm text-gray-600 mb-3">
                        CIDR (/24) 表示子网掩码中连续1的数量。<br>
                        例如: /24 = 255.255.255.0 (24个1)<br>
                        /25 = 255.255.255.128 (25个1)<br>
                        主机数 = 2^(32-n) - 2
                    </p>
                </div>
                <div class="bg-neutral rounded-xl p-4 card-hover">
                    <h4 class="font-medium mb-2">特殊地址</h4>
                    <p class="text-sm text-gray-600 mb-3">
                        0.0.0.0: 默认路由<br>
                        127.0.0.1: 回环地址<br>
                        255.255.255.255: 有限广播<br>
                        10.0.0.0/8: 私有地址<br>
                        192.168.0.0/16: 私有地址
                    </p>
                </div>
            </div>
        </div>
        
        <!-- 页脚 -->
        <footer class="mt-12 text-center text-sm text-gray-500">
            <p>© 2023 IP地址计算器 | 网络基础知识学习工具</p>
        </footer>
    </div>
    
    <script>
        // IP地址计算器逻辑
        document.addEventListener('DOMContentLoaded', function() {
            // DOM元素
            const ipAddressInput = document.getElementById('ipAddress');
            const cidrPrefixSelect = document.getElementById('cidrPrefix');
            const subnetMaskInput = document.getElementById('subnetMask');
            const octetInputs = [
                document.getElementById('octet1'),
                document.getElementById('octet2'),
                document.getElementById('octet3'),
                document.getElementById('octet4')
            ];
            const applySubnetBtn = document.getElementById('applySubnet');
            const calculateBtn = document.getElementById('calculateBtn');
            const splitBtn = document.getElementById('splitBtn');
            const subnetSplitInput = document.getElementById('subnetSplit');
            
            // 结果元素
            const ipClassElement = document.getElementById('ipClass');
            const networkAddressElement = document.getElementById('networkAddress');
            const broadcastAddressElement = document.getElementById('broadcastAddress');
            const hostRangeElement = document.getElementById('hostRange');
            const hostCountElement = document.getElementById('hostCount');
            const binaryMaskElement = document.getElementById('binaryMask');
            const subnetCountElement = document.getElementById('subnetCount');
            const hostsPerSubnetElement = document.getElementById('hostsPerSubnet');
            
            // 可视化元素
            const subnetVisualization = document.getElementById('subnetVisualization');
            const subnetList = document.getElementById('subnetList');
            const subnetGrid = document.getElementById('subnetGrid');
            let subnetChart = null;
            
            // CIDR前缀到子网掩码的映射
            const cidrToMask = {
                '8': '255.0.0.0',
                '16': '255.255.0.0',
                '24': '255.255.255.0',
                '25': '255.255.255.128',
                '26': '255.255.255.192',
                '27': '255.255.255.224',
                '28': '255.255.255.240',
                '29': '255.255.255.248',
                '30': '255.255.255.252'
            };
            
            // 初始化
            updateSubnetMask();
            
            // 事件监听
            cidrPrefixSelect.addEventListener('change', updateSubnetMask);
            applySubnetBtn.addEventListener('click', applyCustomSubnet);
            calculateBtn.addEventListener('click', calculateResults);
            splitBtn.addEventListener('click', splitSubnets);
            
            // 更新子网掩码显示
            function updateSubnetMask() {
                const cidr = cidrPrefixSelect.value;
                subnetMaskInput.value = cidrToMask[cidr] || '';
                updateBinaryMask();
            }
            
            // 更新二进制子网掩码
            function updateBinaryMask() {
                const mask = subnetMaskInput.value;
                if (!mask) return;
                
                const octets = mask.split('.');
                const binary = octets.map(octet => {
                    const bin = parseInt(octet).toString(2);
                    return bin.padStart(8, '0');
                }).join('.');
                
                binaryMaskElement.textContent = binary;
            }
            
            // 应用自定义子网掩码
            function applyCustomSubnet() {
                const octets = octetInputs.map(input => parseInt(input.value) || 0);
                const valid = octets.every(octet => octet >= 0 && octet <= 255);
                
                if (!valid) {
                    alert('请输入有效的子网掩码 (0-255)');
                    return;
                }
                
                const mask = octets.join('.');
                subnetMaskInput.value = mask;
                updateBinaryMask();
                
                // 尝试找到最接近的CIDR前缀
                const binary = octets.map(octet => {
                    const bin = parseInt(octet).toString(2);
                    return bin.padStart(8, '0');
                }).join('');
                
                const ones = (binary.match(/1/g) || []).length;
                if (ones >= 8 && ones <= 30) {
                    cidrPrefixSelect.value = ones;
                }
            }
            
            // 计算IP地址结果
            function calculateResults() {
                const ip = ipAddressInput.value.trim();
                const mask = subnetMaskInput.value.trim();
                
                if (!validateIp(ip) || !validateMask(mask)) {
                    alert('请输入有效的IP地址和子网掩码');
                    return;
                }
                
                const ipParts = ip.split('.').map(Number);
                const maskParts = mask.split('.').map(Number);
                
                // 计算网络地址
                const networkParts = ipParts.map((part, i) => part & maskParts[i]);
                const networkAddress = networkParts.join('.');
                
                // 计算广播地址
                const broadcastParts = ipParts.map((part, i) => part | (255 - maskParts[i]));
                const broadcastAddress = broadcastParts.join('.');
                
                // 计算可用主机范围
                const firstHostParts = [...networkParts];
                firstHostParts[3] += 1;
                const firstHost = firstHostParts.join('.');
                
                const lastHostParts = [...broadcastParts];
                lastHostParts[3] -= 1;
                const lastHost = lastHostParts.join('.');
                
                // 计算可用主机数
                const hostCount = Math.pow(2, 32 - cidrPrefixSelect.value) - 2;
                
                // 确定IP类别
                const firstOctet = ipParts[0];
                let ipClass = '';
                if (firstOctet >= 0 && firstOctet <= 127) ipClass = 'A类';
                else if (firstOctet >= 128 && firstOctet <= 191) ipClass = 'B类';
                else if (firstOctet >= 192 && firstOctet <= 223) ipClass = 'C类';
                else if (firstOctet >= 224 && firstOctet <= 239) ipClass = 'D类 (多播)';
                else ipClass = 'E类 (保留)';
                
                // 更新结果显示
                ipClassElement.textContent = ipClass;
                networkAddressElement.textContent = networkAddress;
                broadcastAddressElement.textContent = broadcastAddress;
                hostRangeElement.textContent = `${firstHost} - ${lastHost}`;
                hostCountElement.textContent = hostCount;
                subnetCountElement.textContent = '1';
                hostsPerSubnetElement.textContent = hostCount;
                
                // 隐藏子网可视化（如果显示）
                subnetVisualization.classList.add('hidden');
                subnetList.classList.add('hidden');
                
                // 添加结果出现动画
                document.getElementById('results').classList.add('animate-fadeIn');
            }
            
            // 划分子网
            function splitSubnets() {
                const numSubnets = parseInt(subnetSplitInput.value) || 0;
                if (numSubnets <= 0 || numSubnets > 32) {
                    alert('请输入1-32之间的子网数量');
                    return;
                }
                
                const ip = ipAddressInput.value.trim();
                const mask = subnetMaskInput.value.trim();
                
                if (!validateIp(ip) || !validateMask(mask)) {
                    alert('请输入有效的IP地址和子网掩码');
                    return;
                }
                
                const ipParts = ip.split('.').map(Number);
                const maskParts = mask.split('.').map(Number);
                
                // 计算网络地址
                const networkParts = ipParts.map((part, i) => part & maskParts[i]);
                const networkAddress = networkParts.join('.');
                
                // 计算需要借用的位数
                let borrowBits = 0;
                while (Math.pow(2, borrowBits) < numSubnets) {
                    borrowBits++;
                }
                
                // 计算新的CIDR前缀
                const newCidr = parseInt(cidrPrefixSelect.value) + borrowBits;
                if (newCidr > 30) {
                    alert('无法划分这么多子网，每个子网至少需要2个主机地址');
                    return;
                }
                
                // 计算新的子网掩码
                const newMaskParts = [];
                let remainingBits = newCidr;
                for (let i = 0; i < 4; i++) {
                    if (remainingBits >= 8) {
                        newMaskParts.push(255);
                        remainingBits -= 8;
                    } else if (remainingBits > 0) {
                        newMaskParts.push(256 - Math.pow(2, 8 - remainingBits));
                        remainingBits = 0;
                    } else {
                        newMaskParts.push(0);
                    }
                }
                const newMask = newMaskParts.join('.');
                
                // 计算每个子网的大小
                const hostsPerSubnet = Math.pow(2, 32 - newCidr) - 2;
                
                // 生成子网列表
                const subnets = [];
                let currentNetwork = networkParts;
                
                for (let i = 0; i < numSubnets; i++) {
                    const subnet = {
                        id: i + 1,
                        network: currentNetwork.join('.'),
                        mask: newMask,
                        cidr: `/${newCidr}`,
                        firstHost: currentNetwork[0] + '.' + currentNetwork[1] + '.' + currentNetwork[2] + '.' + (currentNetwork[3] + 1),
                        lastHost: currentNetwork[0] + '.' + currentNetwork[1] + '.' + currentNetwork[2] + '.' + (currentNetwork[3] + hostsPerSubnet),
                        broadcast: currentNetwork[0] + '.' + currentNetwork[1] + '.' + currentNetwork[2] + '.' + (currentNetwork[3] + hostsPerSubnet + 1),
                        hostCount: hostsPerSubnet
                    };
                    
                    subnets.push(subnet);
                    
                    // 计算下一个子网的网络地址
                    const hostBits = 32 - newCidr;
                    const increment = Math.pow(2, hostBits);
                    
                    let index = 3;
                    currentNetwork[index] += increment;
                    
                    while (currentNetwork[index] > 255 && index > 0) {
                        currentNetwork[index] = 0;
                        index--;
                        currentNetwork[index]++;
                    }
                }
                
                // 更新结果显示
                subnetCountElement.textContent = numSubnets;
                hostsPerSubnetElement.textContent = hostsPerSubnet;
                
                // 显示子网可视化
                subnetVisualization.classList.remove('hidden');
                subnetList.classList.remove('hidden');
                
                // 更新子网网格
                updateSubnetGrid(subnets);
                
                // 更新子网图表
                updateSubnetChart(subnets);
            }
            
            // 更新子网网格显示
            function updateSubnetGrid(subnets) {
                subnetGrid.innerHTML = '';
                
                subnets.forEach(subnet => {
                    const subnetCard = document.createElement('div');
                    subnetCard.className = 'bg-white rounded-xl p-4 shadow-md border border-gray-100 card-hover';
                    subnetCard.innerHTML = `
                        <h4 class="font-medium text-primary mb-2">子网 ${subnet.id}</h4>
                        <div class="text-sm space-y-1">
                            <div class="flex justify-between">
                                <span class="text-gray-500">网络地址:</span>
                                <span class="font-medium">${subnet.network}</span>
                            </div>
                            <div class="flex justify-between">
                                <span class="text-gray-500">子网掩码:</span>
                                <span class="font-medium">${subnet.mask} ${subnet.cidr}</span>
                            </div>
                            <div class="flex justify-between">
                                <span class="text-gray-500">广播地址:</span>
                                <span class="font-medium">${subnet.broadcast}</span>
                            </div>
                            <div class="flex justify-between">
                                <span class="text-gray-500">主机范围:</span>
                                <span class="font-medium">${subnet.firstHost} - ${subnet.lastHost}</span>
                            </div>
                            <div class="flex justify-between">
                                <span class="text-gray-500">主机数:</span>
                                <span class="font-medium">${subnet.hostCount}</span>
                            </div>
                        </div>
                    `;
                    
                    subnetGrid.appendChild(subnetCard);
                });
            }
            
            // 更新子网图表
            function updateSubnetChart(subnets) {
                const ctx = document.getElementById('subnetChart').getContext('2d');
                
                // 销毁旧图表
                if (subnetChart) {
                    subnetChart.destroy();
                }
                
                // 准备数据
                const labels = subnets.map(subnet => `子网 ${subnet.id}`);
                const data = subnets.map(subnet => subnet.hostCount);
                
                // 创建新图表
                subnetChart = new Chart(ctx, {
                    type: 'bar',
                    data: {
                        labels: labels,
                        datasets: [{
                            label: '每个子网的主机数',
                            data: data,
                            backgroundColor: subnets.map((_, i) => `hsl(${i * (360 / subnets.length)}, 70%, 60%)`),
                            borderWidth: 0,
                            borderRadius: 6,
                            barPercentage: 0.6,
                            categoryPercentage: 0.8
                        }]
                    },
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        plugins: {
                            legend: {
                                display: false
                            },
                            tooltip: {
                                backgroundColor: 'rgba(255, 255, 255, 0.9)',
                                titleColor: '#1D2129',
                                bodyColor: '#1D2129',
                                borderColor: 'rgba(22, 93, 255, 0.2)',
                                borderWidth: 1,
                                padding: 10,
                                boxPadding: 5,
                                usePointStyle: true,
                                callbacks: {
                                    label: function(context) {
                                        return `主机数: ${context.raw}`;
                                    }
                                }
                            }
                        },
                        scales: {
                            x: {
                                grid: {
                                    display: false
                                },
                                ticks: {
                                    font: {
                                        size: 12
                                    }
                                }
                            },
                            y: {
                                beginAtZero: true,
                                grid: {
                                    color: 'rgba(0, 0, 0, 0.05)'
                                },
                                ticks: {
                                    font: {
                                        size: 12
                                    },
                                    callback: function(value) {
                                        if (value >= 1000) {
                                            return value / 1000 + 'k';
                                        }
                                        return value;
                                    }
                                }
                            }
                        }
                    }
                });
            }
            
            // 验证IP地址
            function validateIp(ip) {
                const parts = ip.split('.');
                if (parts.length !== 4) return false;
                
                return parts.every(part => {
                    const num = parseInt(part);
                    return !isNaN(num) && num >= 0 && num <= 255;
                });
            }
            
            // 验证子网掩码
            function validateMask(mask) {
                const parts = mask.split('.');
                if (parts.length !== 4) return false;
                
                const binary = parts.map(part => {
                    const num = parseInt(part);
                    if (isNaN(num) || num < 0 || num > 255) return '';
                    return parseInt(num).toString(2).padStart(8, '0');
                }).join('');
                
                // 检查是否是连续的1后跟连续的0
                const firstZero = binary.indexOf('0');
                if (firstZero === -1) return true; // 全1有效
                
                return binary.substring(firstZero).split('').every(bit => bit === '0');
            }
        });
    </script>
</body>
</html>
